package com.lyy.util;

import cn.hutool.core.util.ObjectUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.SneakyThrows;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author JoeZhou
 */
public class JwtUtil {

    /**
     * token令牌服务端秘钥，值随意，但千万不要泄露
     */
    private static final String SECRET_KEY = "my-secret";

    /**
     * 过期时间判定阈值，单位毫秒
     */
    private static final long EXPIRATION_TIME_THRESHOLD = 1000 * 60 * 30L;

    /**
     * 生成用户对应的token字符串，默认将用户的主键，昵称和头像地址添加到token负载中
     *
     * @param userId    用户的主键
     * @param nickName  用户的昵称
     * @param avatarUrl 用户的头像
     * @return token字符串
     */
    public static String build(Integer userId, String nickName, String avatarUrl) {
        if (ObjectUtil.hasNull(userId, nickName, avatarUrl)) {
            throw new RuntimeException("必要参数为空");
        }
        JWTCreator.Builder builder = JWT.create();
        builder.withClaim("userId", userId);
        builder.withClaim("nickName", nickName);
        builder.withClaim("avatarUrl", avatarUrl);
        builder.withSubject("用户登录认证");
        builder.withIssuer("lyy");
        builder.withIssuedAt(new Date());
        builder.withExpiresAt(new Date(System.currentTimeMillis() + (1000 * 60 * 60 * 24L)));
        return builder.sign(Algorithm.HMAC256(SECRET_KEY));
    }

    /**
     * 验证token字符串，验证失败直接抛出异常，
     * 验证成功后判断当前token是否即将过期，若是则生成新的token并返回，否则返回原token值
     *
     * @param token token字符串
     * @return token信息，若expireSoon为true，表示token即将过期，反之表示token不需要刷新
     */
    @SneakyThrows
    public static Map<String, Object> verify(String token) {

        // 检查必要参数
        if (ObjectUtil.isNull(token)) {
            throw new RuntimeException("token字符串为空");
        }

        Map<String, Object> resultMap = new HashMap<>(4);

        // 通过秘钥验证token字符串，验证失败直接抛出异常
        DecodedJWT decodedJwt = JWT.require(Algorithm.HMAC256(SECRET_KEY)).build().verify(token);

        // 判断token是否即将过期
        if (decodedJwt.getExpiresAt().getTime() - System.currentTimeMillis() < EXPIRATION_TIME_THRESHOLD) {
            // 若token过期时间小于指定阈值，则重新生成token并返回
            resultMap.put("expireSoon", true);
            resultMap.put("newToken", JwtUtil.build(
                    decodedJwt.getClaim("userId").asInt(),
                    decodedJwt.getClaim("nickName").asString(),
                    decodedJwt.getClaim("avatarUrl").asString()));
        } else {
            // 若token过期时间大于指定阈值，不需要重新生成token
            resultMap.put("expireSoon", false);
        }

        // 将用户ID存入resultMap中
        resultMap.put("userId", decodedJwt.getClaim("userId").asInt());
        resultMap.put("nickName", decodedJwt.getClaim("nickName").asString());
        resultMap.put("avatarUrl", decodedJwt.getClaim("avatarUrl").asString());

        return resultMap;
    }
}